import siyuan from "siyuan";
//import vis from "vis"; //*不引入，软件前端已引入，仅做提示用，且只能使用全局引入，否则会冲突
import * as sy from "../../common/SiyuanConnect";
import _merge from "lodash/merge";
declare var MenuItem: any;
declare var window;
//*更改node后一定不要忘记newNodes.update(node)
export default class SiyuanSamplePlugin extends Plugin {
  el: HTMLElement;

  constructor() {
    super();
  }

  onload() {
    networkCustom();
    console.log("自定义关系图已加载");
  }

  onunload() {
    console.log("plugin unload");
  }
}

function networkCustom() {
  const button = tooltipButton("自定义关系图");
  button.addEventListener("click", () => {
    onButtonClick();
    //debugConsole(graph);
  });
  siyuan.clientApi.addToolbarRight(button);
  function tooltipButton(label: string): Element {
    const button = document.createElement("button");
    button.setAttribute("aria-label", label);
    button.className = "toolbar__item b3-tooltips b3-tooltips__sw";
    button.innerHTML = `<svg t="1681520366865" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3086" width="200" height="200"><path d="M195.96875 312.5m-85.6875 0a85.6875 85.6875 0 1 0 171.375 0 85.6875 85.6875 0 1 0-171.375 0Z" fill="#9467bd" p-id="3087"></path><path d="M195.96875 226.90625c-35.4375 0-65.90625 21.5625-78.9375 52.3125 14.25-10.5 31.78125-16.6875 50.8125-16.6875 47.34375 0 85.6875 38.34375 85.6875 85.6875 0 11.8125-2.4375 23.0625-6.75 33.375 21.1875-15.5625 34.875-40.6875 34.875-69 0-47.34375-38.34375-85.6875-85.6875-85.6875z" fill="#9467bd" p-id="3088"></path><path d="M195.96875 416.9375c-57.5625 0-104.4375-46.875-104.4375-104.4375 0-57.5625 46.875-104.4375 104.4375-104.4375s104.4375 46.875 104.4375 104.4375c0 57.65625-46.875 104.4375-104.4375 104.4375z m0-171.28125c-36.9375 0-66.9375 30-66.9375 66.9375 0 36.9375 30 66.9375 66.9375 66.9375s66.9375-30 66.9375-66.9375c0-36.9375-30-66.9375-66.9375-66.9375z" fill="#9467bd" p-id="3089"></path><path d="M195.875 828.40625m-85.6875 0a85.6875 85.6875 0 1 0 171.375 0 85.6875 85.6875 0 1 0-171.375 0Z" fill="#d62728" p-id="3090"></path><path d="M196.8125 733.34375c-33.84375 0-63.09375 19.6875-77.0625 48.1875 12.28125-6.75 26.34375-10.6875 41.4375-10.6875 47.34375 0 85.6875 38.34375 85.6875 85.6875 0 13.40625-3.09375 26.15625-8.625 37.5 26.34375-14.625 44.25-42.75 44.25-75-0.09375-47.34375-38.4375-85.6875-85.6875-85.6875z" fill="#d62728" p-id="3091"></path><path d="M195.875 932.75c-57.5625 0-104.4375-46.875-104.4375-104.4375 0-57.5625 46.875-104.4375 104.4375-104.4375s104.4375 46.875 104.4375 104.4375c-0.09375 57.65625-46.875 104.4375-104.4375 104.4375z m0-171.28125c-36.9375 0-66.9375 30-66.9375 66.9375s30 66.9375 66.9375 66.9375 66.9375-30 66.9375-66.9375-30.09375-66.9375-66.9375-66.9375z" fill="#d62728" p-id="3092"></path><path d="M828.125 826.90625m-85.6875 0a85.6875 85.6875 0 1 0 171.375 0 85.6875 85.6875 0 1 0-171.375 0Z" fill="#2ca02c" p-id="3093"></path><path d="M828.125 741.21875c-30.84375 0-57.9375 16.3125-73.03125 40.78125 13.03125-8.0625 28.40625-12.65625 44.90625-12.65625 47.34375 0 85.6875 38.34375 85.6875 85.6875 0 16.40625-4.59375 31.78125-12.65625 44.90625 24.46875-15.09375 40.78125-42.09375 40.78125-73.03125 0-47.34375-38.34375-85.6875-85.6875-85.6875z" fill="#2ca02c" p-id="3094"></path><path d="M828.125 931.25c-57.5625 0-104.4375-46.875-104.4375-104.4375 0-57.5625 46.875-104.4375 104.4375-104.4375s104.4375 46.875 104.4375 104.4375c0 57.65625-46.78125 104.4375-104.4375 104.4375z m0-171.28125c-36.9375 0-66.9375 30-66.9375 66.9375s30 66.9375 66.9375 66.9375 66.9375-30 66.9375-66.9375-30-66.9375-66.9375-66.9375z" fill="#2ca02c" p-id="3095"></path><path d="M825.21875 461.375m-85.6875 0a85.6875 85.6875 0 1 0 171.375 0 85.6875 85.6875 0 1 0-171.375 0Z" fill="#ff7f0e" p-id="3096"></path><path d="M816.78125 408.125c45.46875 13.125 71.71875 60.5625 58.59375 106.03125-1.875 6.46875-4.40625 12.5625-7.59375 18.28125 16.78125-10.78125 29.8125-27.46875 35.71875-48.1875 13.125-45.46875-13.125-92.90625-58.59375-106.03125-39-11.25-79.40625 6.46875-98.4375 40.3125 20.15625-13.03125 45.46875-17.53125 70.3125-10.40625z" fill="#ff7f0e" p-id="3097"></path><path d="M825.21875 565.8125c-57.5625 0-104.4375-46.875-104.4375-104.4375 0-57.5625 46.875-104.4375 104.4375-104.4375 57.5625 0 104.4375 46.875 104.4375 104.4375 0 57.5625-46.875 104.4375-104.4375 104.4375z m0-171.375c-36.9375 0-66.9375 30-66.9375 66.9375 0 36.9375 30 66.9375 66.9375 66.9375s66.9375-30 66.9375-66.9375c0-36.9375-30-66.9375-66.9375-66.9375z" fill="#ff7f0e" p-id="3098"></path><path d="M516.59375 195.59375m-85.6875 0a85.6875 85.6875 0 1 0 171.375 0 85.6875 85.6875 0 1 0-171.375 0Z" fill="#8c564b" p-id="3099"></path><path d="M516.59375 110c-33.5625 0-62.53125 19.21875-76.59375 47.34375 14.15625-10.3125 31.5-16.40625 50.34375-16.40625 47.34375 0 85.6875 38.34375 85.6875 85.6875 0 13.78125-3.28125 26.8125-9.09375 38.34375C588.3125 249.40625 602.28125 224.1875 602.28125 195.6875c0-47.34375-38.4375-85.6875-85.6875-85.6875z" fill="#8c564b" p-id="3100"></path><path d="M516.59375 300.03125c-57.5625 0-104.4375-46.875-104.4375-104.4375 0-57.5625 46.875-104.4375 104.4375-104.4375 57.5625 0 104.4375 46.875 104.4375 104.4375 0 57.5625-46.875 104.4375-104.4375 104.4375z m0-171.28125c-36.9375 0-66.9375 30-66.9375 66.9375 0 36.9375 30 66.9375 66.9375 66.9375s66.9375-30 66.9375-66.9375c0-36.9375-30.09375-66.9375-66.9375-66.9375z" fill="#8c564b" p-id="3101"></path><path d="M512.46875 518.1875m-148.78125 0a148.78125 148.78125 0 1 0 297.5625 0 148.78125 148.78125 0 1 0-297.5625 0Z" fill="#1f77b4" p-id="3102"></path><path d="M512.46875 518.1875m-148.78125 0a148.78125 148.78125 0 1 0 297.5625 0 148.78125 148.78125 0 1 0-297.5625 0Z" fill="#1f77b4" p-id="3103"></path><path d="M512.46875 369.3125c-28.59375 0-55.21875 8.0625-77.90625 22.03125 10.03125-2.15625 20.34375-3.28125 31.03125-3.28125 82.21875 0 148.78125 66.65625 148.78125 148.78125 0 53.625-28.40625 100.59375-70.875 126.84375 67.3125-14.25 117.75-73.96875 117.75-145.59375 0-82.125-66.65625-148.78125-148.78125-148.78125z" fill="#1f77b4" p-id="3104"></path><path d="M512.46875 685.71875c-92.34375 0-167.53125-75.1875-167.53125-167.53125S420.125 350.65625 512.46875 350.65625c92.4375 0 167.53125 75.1875 167.53125 167.53125S604.8125 685.71875 512.46875 685.71875z m0-297.65625c-71.71875 0-130.03125 58.3125-130.03125 130.03125 0 71.71875 58.3125 130.03125 130.03125 130.03125S642.5 589.8125 642.5 518.09375c0-71.625-58.3125-130.03125-130.03125-130.03125z" fill="#1f77b4" p-id="3105"></path><path d="M262.0625 357.875l133.03125 87.375" fill="#1f77b4" p-id="3106"></path><path d="M261.42504883 342.76831055l116.95166016 76.79690552-18.09118652 27.55041503-116.95166015-76.79690551z" fill="#9467bd" p-id="3107"></path><path d="M652.71875 488.5625l86.90625-12.375" fill="#1f77b4" p-id="3108"></path><path d="M756.99625 457.64l5.295 37.125-86.8725 12.3909375-5.295-37.125z" fill="#ff7f0e" p-id="3109"></path><path d="M512.46875 357.875V268.34375" fill="#1f77b4" p-id="3110"></path><path d="M493.71875 268.34375h37.5v89.53125h-37.5z" fill="#8c564b" p-id="3111"></path><path d="M395.09375 628.53125L266.09375 754.25" fill="#1f77b4" p-id="3112"></path><path d="M382.0653125 615.029375l26.1675 26.86125-129.001875 125.6690625-26.1675-26.86125z" fill="#d62728" p-id="3113"></path><path d="M630.96875 629.9375l150.09375 150" fill="#1f77b4" p-id="3114"></path><path d="M644.1828125 616.60625l150.0815625 150.0825-26.51625 26.51625-150.0815625-150.0825z" fill="#2ca02c" p-id="3115"></path></svg>`;
    return button;
  }
}

async function onButtonClick() {
  const { debug模式, 引用关系边初始长度, 父子关系边初始长度 } =
    await getConfig();
  if (debug模式) {
    console.clear();
  }
  //*获取初始值
  let graph =
    window.siyuan.layout.rightDock?.data?.globalGraph ||
    window.siyuan.layout.leftDock?.data?.globalGraph;
  if (!graph) {
    sy.pushErrMsg("请单开全局关系图");
    return;
  }
  //let originNodes = new vis.DataSet(graph.graphData.nodes);
  //let originEdges = new vis.DataSet(graph.graphData.links);
  let network = graph.network as vis.Network;
  //*开始自定义
  let { newNodes, newEdges } = await initGraph();
  if (newNodes.get().length === 0) {
    sy.pushErrMsg("未找到起始节点，初始化失败");
    return;
  }
  setNetworkOption();
  network.on("oncontext", (event: visEvent) => {
    let { node, edge } = eventEle(event);
    右键console(node, edge);
    if (node) {
      onNodeContext(event, node);
    } else if (edge) {
      onEdgeContext(event, edge);
    } else {
      onGlobelContext(event);
    }
  });
  network.on("dragStart", (event: visEvent) => {
    let node = getSelectedNode(event);
    if (node) {
      node.fixed = {
        x: false,
      };
      newNodes.update(node);
    }
  });
  network.on("dragEnd", (event: visEvent) => {
    let node = getSelectedNode(event);
    if (node) {
      //*固定node位置
      let posObj = network.getPositions(node.id);
      node.x = posObj[node.id].x;
      node.fixed = {
        x: true,
      };
      newNodes.update(node);
      //*计算父子位置
      let parents = [node];
      let count = 0;
      let root: GraphNode[] = [node]; //*根节点
      while (parents.length > 0 && count < 100) {
        count++;
        parents = forPorC(parents, edgeType.parent);
        if (parents.length > 0) {
          root = parents;
        }
      }
      let children = root;
      count = 0;
      while (children.length > 0 && count < 100) {
        count++;
        children = forPorC(children, edgeType.child);
      }
    }
    function forPorC(pOrc: GraphNode[], type: edgeType): GraphNode[] {
      let subNodes: GraphNode[] = [];
      for (let parent of pOrc) {
        let edgeIds = network.getConnectedEdges(parent.id) as vis.IdType[];
        let edges = newEdges.get(edgeIds);
        edges = edges.filter((e) => {
          if (type == edgeType.parent) {
            return e.to == parent.id;
          } else {
            return e.from == parent.id;
          }
        });
        edges = edges.filter((e) => {
          return e.type == "child";
        });
        for (let subEdge of edges) {
          let nodeId: vis.IdType;
          if (type == edgeType.parent) {
            nodeId = network.getConnectedNodes(subEdge.id)[0] as vis.IdType; //[fromId, toId]
          } else {
            nodeId = network.getConnectedNodes(subEdge.id)[1] as vis.IdType;
          }
          let subNode = newNodes.get(nodeId);
          if (subNode) {
            setNodeX(subNode, parent, type);
            subNodes.push(subNode);
          }
        }
      }
      return subNodes;
    }
  });
  function getSelectedNode(event: visEvent): GraphNode | null {
    let nodeId = event.nodes[0] as unknown as vis.IdType;
    if (!nodeId) {
      return null; //防止下一步get到全部
    }
    let node = newNodes.get(nodeId);
    return node;
  }
  function 右键console(node: GraphNode | null, edge: GraphLink | null) {
    if (!debug模式) {
      return;
    }
    console.groupCollapsed("右键事件");
    debugConsole("现在的边", newEdges.get());
    debugConsole("现在的节点", newNodes.get());
    debugConsole("选择的节点", node);
    debugConsole("选择的边", edge);
    console.groupEnd();
  }
  function onNodeContext(event: visEvent, node: GraphNode) {
    let contextMenu = new siyuan.Menu("networkCustom_context_node");
    //?这里引入siyuan.MenuItem反而会引起错误
    contextMenu.addItem(
      new MenuItem({
        label: "展开节点",
        click: (e) => {
          expandNode(node);
          contextMenu.close();
        },
      })
    );
    contextMenu.addItem(
      new MenuItem({
        label: "删除节点",
        click: (e) => {
          removeNode(node);
          contextMenu.close();
        },
      })
    );
    contextMenu.showAtPosition({
      x: event.event.x,
      y: event.event.y,
    });
  }
  function onEdgeContext(event: visEvent, edge: GraphLink) {
    let contextMenu = new siyuan.Menu("networkCustom_context_edge");
    //*这里引入siyuan.MenuItem反而会引起错误
    contextMenu.addItem(
      new MenuItem({
        label: "删除边",
        click: (e) => {
          newEdges.remove(edge.id);
          contextMenu.close();
        },
      })
    );
    contextMenu.addItem(
      new MenuItem({
        label: "转为折线/恢复",
        submenu: [
          {
            label: "curvedCW",
            click: (e) => {
              changeEdgeSmooth(edge, "curvedCW");
            },
          },
          {
            label: "curvedCCW",
            click: (e) => {
              changeEdgeSmooth(edge, "curvedCCW");
            },
          },
          {
            label: "恢复",
            click: (e) => {
              changeEdgeSmooth(edge);
            },
          },
        ],
      })
    );
    contextMenu.showAtPosition({
      x: event.event.x,
      y: event.event.y,
    });
  }
  function onGlobelContext(event: visEvent) {
    let contextMenu = new siyuan.Menu("networkCustom_context_globel");
    //?这里引入siyuan.MenuItem反而会引起错误
    contextMenu.addItem(
      new MenuItem({
        label: "展开所有节点(最多20次)",
        click: (e) => {
          expandNodeAll();
          contextMenu.close();
        },
      })
    );
    contextMenu.showAtPosition({
      x: event.event.x,
      y: event.event.y,
    });
  }
  async function expandNodeAll() {
    let nodes = newNodes.get();
    let count = 0;
    while (nodes.length > 0 && count < 20) {
      count++;
      let nodesAdding: GraphNode[] = [];
      for (let node of nodes) {
        nodesAdding = nodesAdding.concat(await expandNode(node));
      }
      nodes = nodesAdding;
    }
  }
  function removeNode(node: GraphNode) {
    let edgeIds = network.getConnectedEdges(node.id);
    newNodes.remove(node.id);
    newEdges.remove(edgeIds);
  }
  function changeEdgeSmooth(edge: GraphLink, type?: string) {
    if (type) {
      edge.smooth = {
        enabled: true,
        type: type,
        forceDirection: "none",
        roundness: 1,
      };
    } else {
      edge.smooth = false;
    }
    newEdges.update(edge);
  }
  async function getConfig() {
    let config = await siyuan.serverApi.getFile(
      "/data/plugins/networkCustom/config.json"
    );
    config = JSON.parse(config);
    return config;
  }
  function 保留原节点() {
    newNodes.add(graph.graphData.nodes);
    newEdges.add(graph.graphData.links);
    network.setData({ nodes: newNodes, edges: newEdges });
  }

  function setNetworkOption() {
    let options = network.options;
    let newOptions: vis.Options = {
      edges: {
        arrows: "to",
      },
      physics: {
        minVelocity: 1,
        stabilization: {
          enabled: true,
          iterations: 200,
        },
      },
    };
    /* if (debug模式) {
      //设置模式
      _merge(newOptions, {
        configure: {
          enabled: true,
          filter: "edges",
          showButton: true,
        },
      });
    }*/
    options = _merge(newOptions, options);
    network.setOptions(options);
  }
  /**
    根据event获取node和edge
  */
  function eventEle(event: visEvent): {
    node: GraphNode | null;
    edge: GraphLink | null;
  } {
    let nodeId = network.getNodeAt(event.pointer.DOM);
    let node: GraphNode | null;
    if (nodeId) {
      node = newNodes.get(nodeId);
    } else {
      node = null;
    }
    let edgeId = network.getEdgeAt(event.pointer.DOM);
    let edge: GraphLink | null;
    if (edgeId) {
      edge = newEdges.get(edgeId);
    } else {
      edge = null;
    }
    return {
      node: node,
      edge: edge,
    };
  }

  function buildNode(block: sy.Block): GraphNode {
    let node = <GraphNode>{};
    node.id = block.id;
    node.path = block.path;
    node.title = block.content;
    node.type = sy.typeAbbrMap[block.type];
    node.label = buildNodeLabel(node);
    return node;
  }

  async function addNodesAndEdges(
    otherBlocks: sy.Block[],
    origin: GraphNode,
    othersType: edgeType
  ): Promise<GraphNode[]> {
    let nodesAdding: GraphNode[] = [];
    if (otherBlocks.length == 0) {
      return [];
    }
    //let edgesData: GraphLink[] = [];
    //let othersData: GraphNode[] = [];
    for (let otherBlock of otherBlocks) {
      //先加节点再加边
      //?要不要先判断再构建
      let other = buildNode(otherBlock);
      let nodeAdded = newNodes.get(other.id);
      if (!nodeAdded) {
        //othersData.push(other);
        setNodeX(other, origin, othersType);
        newNodes.add(other);
        nodesAdding.push(other);
      }
      let edge = buildEdge(origin, other, othersType);
      if (!edge.id) {
        continue;
      }
      let edgeAdded = newEdges.get(edge.id);
      if (!edgeAdded || edgeAdded.length == 0) {
        if (otherBlock.refmarkdown) {
          let edgeLabel = findInlineMemo(otherBlock.refmarkdown);
          edge.label = edgeLabel;
        }
        //edgesData.push(edge);
        newEdges.add(edge);
      }
    }
    return nodesAdding;
    //network.setData({ nodes: newNodes, edges: newEdges });
  }

  function setNodeX(
    node: GraphNode,
    origin: GraphNode,
    nodeType: edgeType
  ): void {
    const space = 200;
    let originX: number;
    if (!origin.x) {
      let posObj = network.getPositions(origin.id);
      originX = posObj[origin.id].x;
    } else {
      originX = origin.x;
    }
    switch (nodeType) {
      case "child":
        node.x = originX + space;
        break;
      case "parent":
        node.x = originX - space;
        break;
    }
    switch (nodeType) {
      case "child":
      case "parent":
        node.fixed = {
          x: true,
        };
        break;
    }
    if (newNodes.get(node.id)) {
      newNodes.update(node);
    }
  }
  async function expandNode(node: GraphNode): Promise<GraphNode[]> {
    let result: GraphNode[] = [];
    let nodesAdding: GraphNode[] = [];
    let originBlock = await sy.getBlockById(node.id);
    if (!originBlock) {
      return [];
    }
    let ParentBlock = await sy.getParentBlock(originBlock);
    if (ParentBlock) {
      nodesAdding = await addNodesAndEdges(
        [ParentBlock],
        node,
        edgeType.parent
      );
      result = result.concat(nodesAdding);
    }
    let ChildrenBlocks = await sy.getChildrenBlocks(node.id);
    nodesAdding = await addNodesAndEdges(ChildrenBlocks, node, edgeType.child);
    result = result.concat(nodesAdding);
    let DefBlocks = await sy.getDefBlocks(node.id);
    nodesAdding = await addNodesAndEdges(DefBlocks, node, edgeType.def);
    result = result.concat(nodesAdding);
    let RefBlocks = await sy.getRefBlocks(node.id);
    nodesAdding = await addNodesAndEdges(RefBlocks, node, edgeType.ref);
    result = result.concat(nodesAdding); // todo 未去重
    return result;
  }
  function buildEdge(
    origin: GraphNode,
    other: GraphNode,
    type: "ref" | "def" | "parent" | "child",
    label?: string
  ): GraphLink {
    let link: GraphLink;
    switch (type) {
      case "ref":
        link = {
          from: origin.id,
          to: other.id,
          ref: true,
          length: 引用关系边初始长度,
          type: "ref",
          id: "",
        };
        break;
      case "def":
        link = {
          from: other.id,
          to: origin.id,
          ref: true,
          length: 引用关系边初始长度,
          type: "ref",
          id: "",
        };
        break;
      case "parent":
        link = {
          from: other.id,
          to: origin.id,
          ref: false,
          length: 父子关系边初始长度,
          type: "child",
          id: "",
        };
        break;
      case "child":
        link = {
          from: origin.id,
          to: other.id,
          ref: false,
          length: 父子关系边初始长度,
          type: "child",
          id: "",
        };
        break;
    }
    if (label) {
      link.label = label;
    }
    link.id = `${link.from}-${link.to}-${link.type}`; //*在此显式的声明id是为了明确什么样的link算重复
    return link;
  }

  function buildNodeLabel(node: GraphNode): string {
    const labelLength = 8; //todo 可以改为自定义
    switch (node.type) {
      case "NodeDocument":
      case "NodeHeading":
      case "NodeParagraph":
        let label = "";
        if (node.label) {
          return node.label;
        }
        if (!node.title) {
          return "";
        }
        if (node.title.length > labelLength) {
          label = node.title.substring(0, labelLength) + "...";
        } else {
          label = node.title;
        }
        return label;
      default:
        return node.type;
    }
  }
  function findInlineMemo(markdown: string): string {
    //!用了正则
    let inlineMemo = markdown.match(/(?<=\^).*?(?=\^)/);
    if (inlineMemo) {
      return inlineMemo[0];
    }
    return "";
  }
  async function initGraph(): Promise<{
    newNodes: nodeDataSet;
    newEdges: edgeDataSet;
  }> {
    let currentBlockId = sy.getFocusNodeId();
    let startBlock: sy.Block | null = null;
    let startNode: GraphNode | null = null;
    let newNodes = new vis.DataSet();
    let newEdges = new vis.DataSet<vis.Edge>();
    if (currentBlockId) {
      startBlock = await sy.getBlockById(currentBlockId);
    }
    if (startBlock) {
      startNode = buildNode(startBlock);
    }
    if (startNode) {
      newNodes.add(startNode);
    }
    network.setData({ nodes: newNodes, edges: newEdges });
    return {
      newNodes: newNodes as nodeDataSet,
      newEdges: newEdges as edgeDataSet,
    };
  }

  //翻译自'\kernel\model\graph.go'
  interface GraphNode extends vis.Node {
    id: string; //`json:"id"`
    box: string; //`json:"box"`
    path: string; //   `json:"path"`
    // todo size: number; //  `json:"size"`
    title: string; //   `json:"title,omitempty"`
    label: string; //  `json:"label"`
    type: string; //  `json:"type"`
    // todo refs: number; //  `json:"refs"`
    // todo defs: number; //  `json:"defs"`
    //todo Color: GraphNodeColor; //`json:"color"`
    isOrigin?: boolean; //*新添加，用于判断是否处理过,待删除，在添加边和节点时判断
  }
  class nodeDataSet extends vis.DataSet<GraphNode> {
    constructor() {
      super();
    }
  }
  interface GraphLink extends vis.Edge {
    id: string;
    //from: string; //       `json:"from"`
    //to: string; //`json:"to"`
    ref: boolean; //`json:"-"`
    //todo color: GraphLinkColor; //`json:"color"`
    // todo arrows: GraphArrows; //`json:"arrows"`
    label?: string;
    length?: number;
    type: "ref" | "child";
  }
  class edgeDataSet extends vis.DataSet<GraphLink> {
    constructor() {
      super();
    }
  }
  function debugConsole(msg: any, obj?: any) {
    if (!debug模式) {
      return;
    }
    if (typeof msg == "string") {
      console.log(`[关系图增强]${msg}`);
    } else {
      console.log(msg);
    }
    if (obj) {
      console.log(obj);
    }
  }
  interface siyuanGraph {
    blockId: string;
    element: HTMLElement;
    graphData: {
      box: string;
      conf: object;
      id: string;
      links: GraphLink[];
      nodes: GraphNode[];
      reqId: number;
    };
    graphElement: HTMLElement;
    inputElement: HTMLElement;
    network: vis.Network;
    panelElement: HTMLElement;
    parent: object;
    rootId?: string;
    timeout: number;
    type: string;
    ws?: any;
  }
  interface visEvent {
    pointer: {
      DOM: {
        x: number;
        y: number;
      };
      canvas: {
        x: number;
        y: number;
      };
    };
    event: PointerEvent;
    nodes: GraphNode[];
    edges: GraphLink[];
  }
  enum edgeType {
    parent = "parent",
    child = "child",
    ref = "ref",
    def = "def",
  }
}
